/*
 * MediaStreamPlayer.cpp
 *
 *  Created on: 2018年8月2日
 *      Author: zhengboyuan
 */
#include "stdafx.h"
#include "MediaStreamPlayer.h"
#include "TFileUtil.h"
#include "TStringUtil.h"
#include <algorithm>
#include <utility>
#include "CLog.h"


MediaStreamPlayer::MediaStreamPlayer():
    m_system(),
    m_stream(),
    m_format(),
	m_hasOpenDecoder(false)
{
}

MediaStreamPlayer::~MediaStreamPlayer()
{
    Close();
}


void MediaStreamPlayer::Close()
{
    if (IsOpen())
    {
        Stop();
    }

    closeStream();

	closeDecoder();
}

bool MediaStreamPlayer::IsOpen()
{
    return isStreamOpen();
}

void MediaStreamPlayer::Stop()
{
	if (IsOpen())
	{
		m_stream->stop();
	}

	MDecode_Stop(m_handle);
}

bool MediaStreamPlayer::Open(nvs::NetVideoSystem* system, nvs::MediaStream* stream)
{
	if (!system || !stream)
	{
		return false;
	}

	m_system = system;
	m_stream = stream;

	if (!isDecoderOpen())
	{
		int rc = m_stream->getFormat(m_format);
		if (rc == 0)
		{
			openDecoder(m_format);
		}
		else
		{
			CLog::warning("MediaStreamPlayer::Open(%d). failed to getFormat of stream 0x%p\n", m_id);
		}
	}

    m_stream->setSink(this);

    return true;
}

bool MediaStreamPlayer::Reconnect(nvs::NetVideoSystem* system, nvs::MediaStream* stream)
{
	closeStream();

	m_system = system;
	m_stream = stream;

	if (!isDecoderOpen())
	{
		int rc = m_stream->getFormat(m_format);
		if (rc == 0)
		{
			openDecoder(m_format);
		}
	}

	m_stream->setSink(this);

	return true;
}

bool MediaStreamPlayer::isStreamOpen()
{
    return m_stream != NULL;
}

bool MediaStreamPlayer::Play()
{
	if (!m_stream)
	{
		return false;
	}

	MediaPlayer::Play();

	int ret = m_stream->play();
	return ret == nvs::NVS_ERR_OK;
}

bool MediaStreamPlayer::Pause(bool toPause)
{
    if (!m_stream)
    {
        return false;
    }

    int ret = nvs::NVS_ERR_OK;
    if (toPause)
    {
        ret = m_stream->pause();
    }
    else
    {
        ret = m_stream->play();
    }

	MediaPlayer::Pause(toPause);

    return ret == nvs::NVS_ERR_OK;
}

bool MediaStreamPlayer::SetScale(double scale)
{
    if (!m_stream)
    {
        return false;
    }
	
	MediaPlayer::SetScale(scale);

    int ret = m_stream->setScale(scale);
    return ret == nvs::NVS_ERR_OK;
}

void MediaStreamPlayer::closeStream()
{
    if (m_stream)
    {
        m_system->closeStream(m_stream);
        m_stream = NULL;
    }

	m_videoFrameCount = 0;
}

int64_t MediaStreamPlayer::GetTime()
{
    if (!m_stream)
    {
        return 0;
    }

    int64_t pts = 0;
    pts = m_stream->getPos();
    return pts;
}

bool MediaStreamPlayer::Seek(int64_t pts)
{
    if (!m_stream)
    {
        return false;
    }

    int ret = m_stream->seek(pts);
    return ret == nvs::NVS_ERR_OK;
}


void MediaStreamPlayer::setFormat(const nvs::StreamFormat& fmt)
{
	CLog::info("MediaStreamPlayer::setFormat(%d). stream: %p, codec: %d\n", m_stream, fmt.codec, fmt.audioCodec, m_id);

	if (!isDecoderOpen())
	{
		memcpy(&m_format, &fmt, sizeof(fmt));

		openDecoder(m_format);

		MediaPlayer::Play();
	}
}

int MediaStreamPlayer::writePacket(const nvs::StreamPacket& packet)
{
	if (packet.flag >= nvs::IMS_FRAME_ALARM_ANALYZE)
	{
		return 0;
	}

	int flags = 0;
	int mediaType = MDECODE_MEDIA_NONE;
	if (packet.flag == nvs::IMS_FRAME_COMPOUND)
	{ 
		mediaType = MDECODE_MEDIA_DATA;

		//comn::FileUtil::write(packet.data, packet.size, "dump.av", true);
	}
	else if (packet.flag >= nvs::IMS_FRAME_VIDEOPARAM)
	{
		//comn::FileUtil::write(packet.data, packet.size, "dump.h264", true);

		mediaType = MDECODE_MEDIA_VIDEO;

		if (packet.flag == nvs::IMS_FRAME_VIDEO_I)
		{
			flags |= MDECODE_FLAG_KEY;
		}

		//CTime t(packet.timestamp / 1000);

		//TRACE("video. size:%d, flag: %d, pts: %lld. clock: %d-%d-%d.%d\n",
		//	packet.size, packet.flag, packet.timestamp,
		//	t.GetHour(), t.GetMinute(), t.GetSecond(), packet.timestamp % 1000);

		//static int s_pktCount = 0;
		//s_pktCount++;

		//if (s_pktCount >= 360)
		//{
		//	s_pktCount = 0;

		//	endOfStream();
		//}
	}
	else
	{
		//comn::FileUtil::write(packet.data, packet.size, "dump.a", true);

		mediaType = MDECODE_MEDIA_AUDIO;

		if (m_format.audioChannel == 0)
		{
			onFoundAudio(packet.flag);
		}

	}

	MDecode_InputData(m_handle, mediaType, packet.data, packet.size, packet.timestamp * 1000, flags);

    return 0;
}

void MediaStreamPlayer::endOfStream(int reason)
{
    onEndOfStream(reason);
}

void MediaStreamPlayer::copyFormat(MDecode_Format& dest, nvs::StreamFormat& src)
{
	dest.codec = convertCodec(src.codec);
	dest.audioCodec = convertCodec(src.audioCodec);
	dest.channels = src.audioChannel;
	dest.samplerate = src.audioSampleRate;
	dest.prop = src.prop;
	dest.propSize = src.propSize;

	if (dest.channels <= 0 || dest.samplerate <= 0)
	{
		dest.audioCodec = MDECODE_CODEC_NONE;
	}
}

struct CodecPair
{
	MDecode_CodecType mcodec;
	nvs::CompressionCodec nvsCodec;
};

static CodecPair s_codecs[] = 
{ 
	{ MDECODE_CODEC_H264, nvs::HIK_MEDIA_CODEC },

	{ MDECODE_CODEC_MPEG4, nvs::STANDARD_MPEG4_CODEC },
	{ MDECODE_CODEC_H264, nvs::STANDARD_H264_CODEC },
	{ MDECODE_CODEC_HEVC, nvs::STANDARD_H265_CODEC },
	
	{ MDECODE_CODEC_G711U, nvs::G711U_CODEC },
	{ MDECODE_CODEC_G711A, nvs::G711A_CODEC },

	{ MDECODE_CODEC_AAC, nvs::AAC_ADTS_CODEC}
};

int MediaStreamPlayer::convertCodec(int nvsCodec)
{
	size_t count = sizeof(s_codecs) / sizeof(s_codecs[0]);
	for (size_t i = 0; i < count; i++)
	{
		if (s_codecs[i].nvsCodec == nvsCodec)
		{
			return s_codecs[i].mcodec;
		}
	}
	return MDECODE_CODEC_NONE;
}

bool MediaStreamPlayer::openDecoder(nvs::StreamFormat& format)
{
	comn::AutoCritSec lock(m_csDecoder);
	if (m_hasOpenDecoder)
	{
		return true;
	}

	if (format.codec < 0 && format.audioCodec < 0)
	{
		return false;
	}


	MDecode_Format fmt = MDecode_Format();
	copyFormat(fmt, format);

	BOOL ret = MDecode_OpenStream(m_handle, &fmt);
	if (ret != TRUE)
	{
		return false;
	}

	m_hasOpenDecoder = true;

	onOpen();

	return true;
}

bool MediaStreamPlayer::isDecoderOpen()
{
	comn::AutoCritSec lock(m_csDecoder);
	return m_hasOpenDecoder;
}

void MediaStreamPlayer::closeDecoder()
{
	comn::AutoCritSec lock(m_csDecoder);
	if (m_hasOpenDecoder)
	{
		MDecode_CloseStream(m_handle);
		m_hasOpenDecoder = false;
	}
}

void MediaStreamPlayer::onFoundAudio(int flag)
{
	MDecode_CodecType codec = MDECODE_CODEC_NONE;
	if (flag == nvs::IMS_FRAME_G711U)
	{
		m_format.audioCodec = nvs::G711U_CODEC;
		m_format.audioChannel = 1;
		m_format.audioSampleRate = 8000;

		codec = MDECODE_CODEC_G711U;
	}
	else if (flag == nvs::IMS_FRAME_G711A)
	{
		m_format.audioCodec = nvs::G711A_CODEC;
		m_format.audioChannel = 1;
		m_format.audioSampleRate = 8000;

		codec = MDECODE_CODEC_G711A;
	}
	else
	{
		return;
	}

	MDecode_SetAudioFormat(m_handle, codec, m_format.audioChannel, m_format.audioSampleRate, NULL, 0);
}

nvs::NetVideoSystem* MediaStreamPlayer::getSystem()
{
	return m_system;
}